#include "Parallelepipede.hpp"

#include "Utils/Erreur.hpp"

#include <algorithm>

using namespace std;

/**
 * Constructeur permettant de définir les différents plans qui
 * aboutissent au Parallelepipede. Les trois vecteurs donnés doivent
 * être non-coplanaires.
 *
 * @param a un des sommet du Parallelepipede.
 * @param x une des arrêtes.
 * @param y une des arrêtes.
 * @param z une des arrêtes.
 */
Parallelepipede::Parallelepipede(const Coord& a, const Vecteur& x,
				 const Vecteur& y, const Vecteur& z)
{
  try
    {
      // on cree les 6 plans
      listPlan.push_back(Plan::SharedPtr (new Plan(Oriente(x^y,'n',z),a)));
      Coord a1=a+z;
      listPlan.push_back(Plan::SharedPtr (new Plan(Oriente(x^y,'p',z),a1)));
      listPlan.push_back(Plan::SharedPtr (new Plan(Oriente(x^z,'n',y),a)));
      Coord a2=a+y;
      listPlan.push_back(Plan::SharedPtr (new Plan(Oriente(x^z,'p',y),a2)));
      listPlan.push_back(Plan::SharedPtr (new Plan(Oriente(y^z,'n',x),a)));
      Coord a3=a+x;
      listPlan.push_back(Plan::SharedPtr (new Plan(Oriente(y^z,'p',x),a3)));
      // on fait l'intersection de tous ces plans
      listFC.push_back(FormeComposee::SharedPtr (new FormeComposee(inter,listPlan[0].get(),listPlan[1].get())));
      listFC.push_back(FormeComposee::SharedPtr (new FormeComposee(inter,listPlan[2].get(),listPlan[3].get())));
      listFC.push_back(FormeComposee::SharedPtr (new FormeComposee(inter,listPlan[4].get(),listPlan[5].get())));
      listFC.push_back(FormeComposee::SharedPtr (new FormeComposee(inter,listFC[0].get(),listFC[1].get())));
      form[0]=listFC[2].get();
      form[1]=listFC[3].get();
      op=inter;
    }
  catch(Erreur& tmp)
    {
      tmp.ecrire("Erreur.txt");
      cerr << "Impossible de construire parallelepipede\n"
	   << "Consulter le log pour plus d'info" << endl;
    }

  Nom = "Parallelepipede";
  PseudoCentre = a + .5 * ( x + y + z );

  vector< double > distances;

  distances.push_back( Vecteur( PseudoCentre, a ).norme() );
  distances.push_back( Vecteur( PseudoCentre, a + x ).norme() );
  distances.push_back( Vecteur( PseudoCentre, a + y ).norme() );
  distances.push_back( Vecteur( PseudoCentre, a + z ).norme() );

  PseudoRayon = sqrt( *max_element( distances.begin(), distances.end() ) );
}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
Parallelepipede::~Parallelepipede()
{}

/**
 * Permet de renverser un vecteur par rapport à un vecteur de
 * référence. On peut choisir de garder l'orientation (donnée par le
 * produit scalaire) ou de l'inverser.
 *
 * Peut lancer une Erreur si l'opération demandée n'est pas définie.
 *
 * @param v1 vecteur que l'on veut orienter.
 * @param op "signe" de la projection : \e 'p' => positive, \e 'n' \f$
 * \Rightarrow \f$
 * négative.
 * @param v2 vecteur de référence, servant à donner l'orientation.
 * @return un vecteur unitaire parallèle à v1, orienté de telle sorte
 * que son produit scalaire avec v2 ait le signe désiré.
 */
VecteurU Parallelepipede::Oriente(const Vecteur& v1, const char& op,
				  const Vecteur &v2) const
{
  if(op=='p') //projection positive
    {
      if((v1*v2)<0)
	{
	  VecteurU v(-v1);
	  return v;
	}
      else if((v1*v2)>0)
	{
	  VecteurU v(v1);
	  return v;
	}
      else
	{
	  Erreur err(true, 50, "Le cube est en 2D", "Parallelepipede::Oriente");
	  throw err;
	}
    }
  if(op=='n') //projection negative
    {
      if((v1*v2)>0)
	{
	  VecteurU v(-v1);
	  return v;
	}
      else if((v1*v2)<0)
	{
	  VecteurU v(v1);
	  return v;
	}
      else
	{
	  Erreur err(true, 50, "Le cube est en 2D", "Parallelepipede::Oriente");
	  throw err;
	}
    }
  else
    {
      Erreur err(true, 49, "Operateur de projection positive ou negative inconnu", "Parallelepipede::Oriente");
      throw err;
    }
}
